package no.gnet.edvd.web.interceptor;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import no.gnet.edvd.web.util.Constants;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.restfb.json.JsonObject;

public class SessionInterceptor implements HandlerInterceptor {

	private static Logger logger = Logger.getLogger(SessionInterceptor.class);

	
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object arg2, Exception arg3)
			throws Exception {
	}

	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
			Object arg2, ModelAndView arg3) throws Exception {

	}

	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		HttpSession session = request.getSession(true);
		logger.debug("Incoming request, session ");
		Enumeration<String> attributeNames = session.getAttributeNames();
		while (attributeNames.hasMoreElements()) {
			String key = attributeNames.nextElement();
			logger.debug("Session attribute, key=" + key
					+ session.getAttribute(key));
		}
		Enumeration<String> paramNames = request.getParameterNames();
		while (paramNames.hasMoreElements()) {
			String key = paramNames.nextElement();
			logger.debug("Request param, key=" + key
					+ request.getParameter(key));
		}
		Cookie[] cookies = request.getCookies();
		if (cookies != null) {
			for (int i = 0; i < cookies.length; i++) {
				logger.debug("Cookie, name= " + cookies[i].getName()
						+ ", val=" + cookies[i].getValue());
			}
		}

		logger.debug("FB user: " + getFBUserFromCookie(request));

		return true;
	}

	public String getFBUserFromCookie(HttpServletRequest request)
			throws Exception {
		Cookie fbCookie = getFBCookie(request);

		if (fbCookie == null)
			return null;

		// gets cookie value
		String fbCookieValue = fbCookie.getValue();

		// splits it.
		String[] stringArgs = fbCookieValue.split("\\.");
		String encodedPayload = stringArgs[1];

		String payload = base64UrlDecode(encodedPayload);

		// gets the js object from the cookie
		JsonObject data = new JsonObject(payload);

		return data.getString("user_id");

	}

	public static boolean ValidateFBCookie(HttpServletRequest request)
			throws Exception {

		Cookie fbCookie = getFBCookie(request);

		if (fbCookie == null)
			throw new NotLoggedInFacebookException();

		// gets cookie information
		String fbCookieValue = fbCookie.getValue();

		String[] stringArgs = fbCookieValue.split("\\.");
		String encodedSignature = stringArgs[0];
		String encodedPayload = stringArgs[1];

		// decode
		String sig = base64UrlDecode(encodedSignature);
		String payload = base64UrlDecode(encodedPayload);

		// gets the js object from the cookie
		JsonObject data = new JsonObject(payload);

		if (!data.getString("algorithm").equalsIgnoreCase("HMAC-SHA256")) {
			return false;
		}

		SecretKey key = new SecretKeySpec(
				Constants.FacebookApiSecret.getBytes(), "hmacSHA256");

		Mac hmacSha256 = Mac.getInstance("hmacSHA256");
		hmacSha256.init(key);
		// decode the info.
		byte[] mac = hmacSha256.doFinal(encodedPayload.getBytes());

		String expectedSig = new String(mac);

		// compare if the spected sig is the same than in the cookie.
		return expectedSig.equals(sig);

	}

	public static String getFBAccessToken(HttpServletRequest request)
			throws Exception {
		Cookie fbCookie = getFBCookie(request);

		String fbCookieValue = fbCookie.getValue();

		String[] stringArgs = fbCookieValue.split("\\.");
		String encodedPayload = stringArgs[1];

		String payload = base64UrlDecode(encodedPayload);

		// gets the js object from the cookie
		JsonObject data = new JsonObject(payload);

		String authUrl = getAuthURL(data.getString("code"));
		URL url = new URL(authUrl);
		URI uri = new URI(url.getProtocol(), url.getHost(), url.getPath(),
				url.getQuery(), null);
		String result = readURL(uri.toURL());

		String[] resultSplited = result.split("&");

		return resultSplited[0].split("=")[1];

	}

	// creates the url for calling to oauth.
	public static String getAuthURL(String authCode) {
		String url = "https://graph.facebook.com/oauth/access_token?client_id="
				+ Constants.FacebookApiKey + "&redirect_uri=&client_secret="
				+ Constants.FacebookApiSecret + "&code=" + authCode;

		return url;
	}

	// reads the url.
	private static String readURL(URL url) throws IOException {

		InputStream is = url.openStream();

		InputStreamReader inStreamReader = new InputStreamReader(is);
		BufferedReader reader = new BufferedReader(inStreamReader);

		String s = "";

		int r;
		while ((r = is.read()) != -1) {
			s = reader.readLine();
		}

		reader.close();
		return s;
	}

	private static String base64UrlDecode(String input) {
		String result = null;
		Base64 decoder = new Base64(true);
		byte[] decodedBytes = decoder.decode(input);
		result = new String(decodedBytes);
		return result;
	}

	private static Cookie getFBCookie(HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();

		if (cookies == null)
			return null;

		Cookie fbCookie = null;

		for (Cookie c : cookies) {
			if (c.getName().equals("fbsr_" + Constants.FacebookApiKey)) {
				fbCookie = c;
			}
		}

		return fbCookie;
	}

}
